#include "MyShellAssistFunctions.h"
#include "MyShellGlobalVariables.h"

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <termios.h>
#include <unistd.h>
#include <sys/time.h>

MY_SHELL_NAMESPACE_BEGIN
// 打印信息
void printInfo()
{
    // 打印用户名
    std::cerr << "\033[46m" ;
    std::cerr << mysh::cur_user_info->pw_name;
    // 打印第一个分隔符
    std::cerr << "\033[45m";
    std::cerr << "@";
    // 打印主机名
    std::cerr << "\033[42m";
    std::cerr << mysh::cur_host_name;
    // 打印第二个分隔符
    std::cerr << "\033[45m";
    std::cerr << ":";
    // 打印当前工作路径
    std::cerr << "\033[43m";
    std::cerr << mysh::resource_manager->root_path.string();
    // 换行
    std::cerr << "\033[0m";
    std::cerr << " \n";
    // 打印权级，$为普通用户，#为root
    std::cerr << "\033[42m";
    std::cerr << (mysh::cur_uid != 0 ? "$>" : "#>");
    // 打印的信息与输入的命令分开
    std::cerr << "\033[0m";
    std::cerr << " ";
    std::cerr.flush();
}

// 开启输入输出原始模式
void enableRawMode()
{
    termios term;
    tcgetattr(STDIN_FILENO, &term);
    term.c_lflag &= ~(ECHO);
    term.c_lflag &= ~(ICANON);
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
}

// 关闭输入输出原始模式
void disableRawMode()
{
    termios term;
    tcgetattr(STDIN_FILENO, &term);
    term.c_lflag |= (ECHO);
    term.c_lflag |= (ICANON);
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
}

// 输入命令
std::string inputCommand()
{
    // 当前输入的命令
    std::string cur_command = std::string();
    // 当前光标在输入的命令上的位置
    std::int32_t index_of_command = 0;
    // 用于存储当前输入的字符或字符序列
    char cur_ch[4];
    // 用于存储上一次输入的字符，其实是为了方便tab补全
    char pre_ch = 0;
    // 开启输入输出原始模式
    mysh::enableRawMode();
    while (1)
    {
        std::cerr.flush();
        memset(cur_ch, 0, sizeof(cur_ch));
        // 从标准输入读取一个字节大小的字符并存入cur_ch[0]
        if (read(STDIN_FILENO, &cur_ch[0], 1) == 1) 
        {
            // 以'\033'开头的字符序列
            if (cur_ch[0] == '\033') 
            { 
                if (read(STDIN_FILENO, &cur_ch[1], 1) == 1 && cur_ch[1] == '[') 
                {
                    if (read(STDIN_FILENO, &cur_ch[2], 1) == 1) 
                    {
                        switch (cur_ch[2]) 
                        {
                        case 'A':
                            // 当前按下的是上键，表示往前查询一个命令历史记录
                            mysh::mysh_history_index--;
                            // 命令历史记录是个环，索引小于零则索引回到末尾
                            if (mysh::mysh_history_index < 0)
                            {
                                mysh::mysh_history_index = mysh::mysh_history_commands.size() - 1;
                            }
                            // 命令历史记录索引在有效范围内时，清除当前输入的命令并显示索引处的命令历史记录
                            if (mysh::mysh_history_index >= 0 && mysh::mysh_history_index < mysh::mysh_history_commands.size())
                            {
                                // 光标左移至命令索引为0的位置
                                for (std::int32_t i = 0; i < index_of_command; i++)
                                {
                                    std::cerr << "\033[D" << std::flush;
                                }
                                // 清除光标到行尾的内容
                                std::cerr << "\033[K" << std::flush;
                                // 输出命令历史记录，当前命令变为显示出来的命令历史，命令索引放到命令末尾
                                std::cerr << mysh::mysh_history_commands[mysh::mysh_history_index];
                                cur_command = mysh::mysh_history_commands[mysh::mysh_history_index];
                                index_of_command = cur_command.size();
                            }
                            break;
                        case 'B':
                            // 当前按下的是下键
                            // 命令索引不在末尾时，将命令索引和光标都放到命令末尾
                            if (index_of_command != cur_command.size())
                            {
                                for (std::int32_t i = index_of_command; i < cur_command.size(); i++)
                                {
                                    std::cerr << "\033[C" << std::flush;
                                }
                                index_of_command = cur_command.size();
                            }
                            // 命令索引在末尾时，向后查询命令历史记录
                            else
                            {
                                mysh::mysh_history_index++;
                                // 命令历史记录是个环，若命令历史记录索引到达末尾之外，则命令历史记录索引回到0
                                if (mysh::mysh_history_index >= mysh::mysh_history_commands.size())
                                {
                                    mysh::mysh_history_index = 0;
                                }
                                // 命令历史记录索引在有效范围内时，清除当前输入的命令并显示索引处的命令历史记录
                                if (mysh::mysh_history_index >= 0 && mysh::mysh_history_index < mysh::mysh_history_commands.size())
                                {
                                    // 光标左移至0
                                    for (std::int32_t i = 0; i < index_of_command; i++)
                                    {
                                        std::cerr << "\033[D" << std::flush;
                                    }
                                    // 清除光标到行尾的内容
                                    std::cerr << "\033[K" << std::flush;
                                    // 输出命令历史记录，当前命令变为显示出来的命令历史，命令索引放到命令末尾
                                    std::cerr << mysh::mysh_history_commands[mysh::mysh_history_index];
                                    cur_command = mysh::mysh_history_commands[mysh::mysh_history_index];
                                    index_of_command = cur_command.size();
                                }
                            }
                            break;
                        case 'C': 
                            // 当前按下的是右键
                            if (index_of_command < cur_command.size())
                            {
                                std::cerr << "\033[C" << std::flush;
                                index_of_command++;
                            }
                            break;
                        case 'D': 
                            // 当前按下的是左键
                            if (index_of_command > 0)
                            {
                                std::cerr << "\033[D" << std::flush;
                                index_of_command--;
                            }
                            break;
                        }
                    }
                }
                pre_ch = 0;
            } 
            // 当前按下的是Backspace，不过\b似乎不是Linux系统下对应于Backspace的字符
            else if (cur_ch[0] == 127 || cur_ch[0] == '\b') 
            {
                // 当前命令索引小于等于零，无可删除字符
                if (index_of_command <= 0)continue;
                // 当前命令不为空且命令索引在有效范围内
                if (!cur_command.empty())
                {
                    // 清除当前光标至行尾的内容并删除光标前的一个字符
                    std::cerr << "\033[K" << std::flush;
                    cur_command.erase(index_of_command - 1, 1);
                    std::cerr << "\b \b" << std::flush;
                    index_of_command--;
                    // 保存当前光标位置
                    std::cerr << "\033[s" << std::flush;
                    // 输出原本光标之后的内容
                    for (std::int32_t i = index_of_command; i < cur_command.size(); i++)
                    {
                        std::cerr << cur_command[i];
                    }
                    // 还原光标位置
                    std::cerr << "\033[u" << std::flush;
                }
                pre_ch = 0;
            }
            // 当前按下的是Tab键
            else if (cur_ch[0] == '\t') 
            {
                // 若命令索引不在当前命令末尾，则将光标、命令索引都移至当前命令末尾
                if (index_of_command != cur_command.size())
                {
                    for (std::int32_t i = index_of_command; i < cur_command.size(); i++)
                    {
                        std::cerr << "\033[C" << std::flush;
                    }
                    index_of_command = cur_command.size();
                }
                // 上一次按下Tab键的微秒数
                static unsigned long long last_time = 0;
                // 延迟（距上一次按下Tab键）
                static unsigned long long delay = 0;
                // 获取当前微秒数
                struct timeval time_val;
                gettimeofday(&time_val, NULL);
                unsigned long long cur_time = time_val.tv_sec * 1e6 + time_val.tv_usec;
                // 若不是第一次按下Tab键，则计算延迟
                if (last_time != 0)
                {
                    delay = cur_time - last_time;
                }
                last_time = cur_time;
                // 按下Tab键之前的命令字符串
                static std::string command_pre_tab = std::string();
                // 按下Tab键之前的命令字符串索引
                static std::int32_t index_of_command_pre_tab = 0;
                // 上一次按下的键不是Tab键，则需保存当前命令、当前命令索引及当前光标位置，否则恢复保存的命令、命令索引及光标位置
                if (pre_ch != '\t')
                {
                    command_pre_tab = cur_command;
                    index_of_command_pre_tab = index_of_command;
                    std::cerr << "\033[s" << std::flush;
                }
                else
                {
                    cur_command = command_pre_tab;
                    index_of_command = index_of_command_pre_tab;
                    std::cerr << "\033[u" << std::flush;
                    std::cerr << "\033[K" << std::flush;
                }
                // 获取当前命令串被跳过字符隔开后的最后一个子串，存入suf_command
                std::string suf_command = std::string();
                std::int32_t i = cur_command.size() - 1;
                while (i >= 0 && cur_command[i] != mysh::default_skip_char)i--;
                i++;
                for (; i < cur_command.size(); i++)
                {
                    suf_command += cur_command[i];
                }
                // 若当前还没有输入任何命令，则继续下一个字符从输入
                if (suf_command == "")continue;
                // 若当前输入的命令被跳过字符分割后只有一个子串，则获取所有可能的命令后缀
                else if (suf_command.size() == cur_command.size())
                {
                    // suf_command作为前缀时的内部命令字典树
                    myds::Trie* node_internal = nullptr;
                    // suf_command作为前缀时的外部命令字典树
                    myds::Trie* node_external = nullptr;
                    // suf_command作为前缀时的系统命令字典树
                    std::vector<myds::Trie*> node_system = std::vector<myds::Trie*>();
                    // 是否找到命令suf_command作为前缀时的字典树
                    static bool if_found_trie = false;
                    // 上一次按下的键不是Tab才找命令前缀字典树
                    if (pre_ch != '\t')
                    {
                        if_found_trie = false;
                        node_internal = mysh::internal_commands_trie->search(suf_command);
                        if_found_trie = (node_internal != nullptr);
                        node_external = mysh::external_commands_trie->search(suf_command);
                        if_found_trie = (node_external != nullptr);
                        for (myds::Trie* cur_trie : mysh::system_commands_tries)
                        {
                            myds::Trie* cur_found_trie = cur_trie->search(suf_command);
                            if (cur_found_trie != nullptr)
                            {
                                node_system.push_back(cur_found_trie);
                                if_found_trie = true;
                            }
                        }
                    }
                    // 若suf_command是某些命令的前缀，则获取这个前缀的所有后缀
                    if (if_found_trie)
                    {
                        // 用于保存后缀的数组
                        static std::vector<std::string> suffix_commands = std::vector<std::string>();
                        // 若前一个按下的键不是Tab，则表示需要重新查找后缀
                        if (pre_ch != '\t')
                        {
                            suffix_commands.clear();
                            if (node_internal != nullptr)
                            {
                                node_internal->getSuffix(suffix_commands);
                            }
                            if (node_external != nullptr)
                            {
                                node_external->getSuffix(suffix_commands);
                            }
                            for (myds::Trie* cur_trie : node_system)
                            {
                                cur_trie->getSuffix(suffix_commands);
                            }
                        }
                        // 若存在后缀
                        if (suffix_commands.size() != 0)
                        {
                            // 后缀数组的索引
                            static int32_t suffix_commands_index = -1;
                            // 保存补全前的命令
                            static std::string cur_command_temp = std::string();
                            // 若上一个按下的键不是Tab，则后缀数组索引初始化为0，并保存当前命令
                            if (pre_ch != '\t')
                            {
                                suffix_commands_index = 0;
                                cur_command_temp = cur_command;
                            }
                            // 补全命令
                            else
                            {
                                // 延时在1e5到2e5之间，表示双击Tab，显示所有后缀，显示完成后重新输出shell信息即当前输入的命令，并重新保存光标位置
                                if (delay >= 1e5 && delay <= 2e5)
                                {
                                    suffix_commands_index--;
                                    std::cerr << std::endl;
                                    bool blue_purple = false;
                                    for (const std::string& suffix_command : suffix_commands)
                                    {
                                        if (!blue_purple)
                                        {
                                            std::cerr << "\033[34m";
                                        }
                                        else
                                        {
                                            std::cerr << "\033[35m";
                                        }
                                        std::cerr << suffix_command << "\t";
                                        std::cerr << "\033[0m";
                                        blue_purple ^= 1;
                                    }
                                    std::cerr << std::endl;
                                    mysh::printInfo();
                                    std::cerr << cur_command << std::flush;
                                    std::cerr << "\033[s" << std::flush;
                                }
                                // 循环获取下一个后缀索引
                                suffix_commands_index++;
                                suffix_commands_index %= suffix_commands.size();
                                // 将当前命令返回到补全之前
                                cur_command = cur_command_temp;
                            }
                            // 输出后缀（补全）
                            std::cerr << suffix_commands[suffix_commands_index];
                            cur_command += suffix_commands[suffix_commands_index];
                            index_of_command = cur_command.size();
                        }
                    }
                }
                // 不止有一个子串，则获取所有当前路径下的文件名后缀，不全思路与上一个分支相同，不再赘述
                else
                {
                    static std::vector<std::string> suffix_commands_rm = std::vector<std::string>();
                    std::string suf_command_root = suf_command;
                    std::string suf_command_suf = std::string();
                    while (!suf_command_root.empty() && suf_command_root.back() != '/')
                    {
                        suf_command_suf += suf_command_root.back();
                        suf_command_root.pop_back();
                    }
                    std::reverse(suf_command_suf.begin(), suf_command_suf.end());
                    mysh::ResourceManager* cur_resource_manager = nullptr;
                    bool if_delete = !suf_command_root.empty();
                    if (!if_delete)
                    {
                        cur_resource_manager = mysh::resource_manager;
                    }
                    else
                    {
                        cur_resource_manager = new mysh::ResourceManager(suf_command_root, false);
                    }
                    if (pre_ch != '\t')
                    {
                        suffix_commands_rm.clear();
                        suffix_commands_rm = cur_resource_manager->getSuffix(suf_command_suf);
                        if (if_delete)
                        {
                            delete cur_resource_manager;
                        }
                    }
                    if (suffix_commands_rm.size() != 0)
                    {
                        static int32_t suffix_commands_index_rm = -1;
                        static std::string cur_command_temp_rm = std::string();
                        if (pre_ch != '\t')
                        {
                            suffix_commands_index_rm = 0;
                            cur_command_temp_rm = cur_command;
                        }
                        else
                        {
                            if (delay >= 1e5 && delay <= 2e5)
                            {
                                suffix_commands_index_rm--;
                                bool blue_purple = false;
                                std::cerr << std::endl;
                                for (const std::string& suffix_command : suffix_commands_rm)
                                {
                                    if (!blue_purple)
                                    {
                                        std::cerr << "\033[34m";
                                    }
                                    else
                                    {
                                        std::cerr << "\033[35m";
                                    }
                                    std::cerr << suffix_command << "\t";
                                    std::cerr << "\033[0m";
                                    blue_purple ^= 1;
                                }
                                std::cerr << std::endl;
                                mysh::printInfo();
                                std::cerr << cur_command << std::flush;
                                std::cerr << "\033[s" << std::flush;
                            }
                            suffix_commands_index_rm++;
                            suffix_commands_index_rm %= suffix_commands_rm.size();
                            cur_command = cur_command_temp_rm;
                        }
                        std::cerr << suffix_commands_rm[suffix_commands_index_rm];
                        cur_command += suffix_commands_rm[suffix_commands_index_rm];
                        index_of_command = cur_command.size();
                    }
                }
                pre_ch = '\t';
            } 
            // 当前按下的是回车
            else if (cur_ch[0] == '\n' || cur_ch[0] == '\r') 
            {
                // 换行
                std::cerr << std::endl;
                // 当前命令不空则将当前命令保存进命令历史记录
                if (cur_command != "") mysh::mysh_history_commands.push_back(cur_command);
                // 保证命令历史记录数组的大小与配置文件中的设定相同
                if (mysh::mysh_history_commands.size() > mysh::mysh_history_max_size)
                {
                    mysh::mysh_history_commands.erase(mysh::mysh_history_commands.begin());
                }
                // 命令历史记录索引放到命令历史记录数组的末尾
                mysh::mysh_history_index = mysh::mysh_history_commands.size();
                break;
            }
            // 其他键
            else 
            {
                // 清除当前光标至行尾的内容
                std::cerr << "\033[K" << std::flush;
                // 插入新输入的字符
                cur_command.insert(index_of_command, 1, cur_ch[0]);
                index_of_command++;
                std::cerr << cur_ch[0] << std::flush;
                // 输出当前命令在命令索引之后的所有内容并将光标会退
                std::cerr << "\033[s" << std::flush;
                for (std::int32_t i = index_of_command; i < cur_command.size(); i++)
                {
                    std::cerr << cur_command[i];
                }
                std::cerr << "\033[u" << std::flush;
                pre_ch = 0;
            }
        }
    }
    // 关闭输入输出原始模式
    mysh::disableRawMode();
    return cur_command;
}

// 依据skip拆分字符串command并存入字符串数组中
std::vector<std::string>& split(const std::string& command, char skip)
{
    static std::vector<std::string> ans = std::vector<std::string>();
    ans.clear();
    // 辅助字符串
    std::string assist = "";
    std::int32_t i = 0;
    std::size_t n = command.size();
    while (i < n)
    {
        // 辅助字符串置空
        assist = "";
        // 跳过跳过字符
        while (i < n && command[i] == skip)i++;
        // 非跳过字符加入辅助字符串
        while (i < n && command[i] != skip)
        {
            assist += command[i];
            i++;
        }
        // 索引到末尾或再次遇到跳过字符且辅助字符串不为空
        if (!assist.empty())
        {
            // 将辅助字符串加入答案字符串数组中
            ans.push_back(assist);
        }
    }
    return ans;
}

// 依据skip合并字符串数组command并存入字符串中
std::string& join(const std::vector<std::string>& command, char skip)
{
    static std::string ans = std::string();
    ans.clear();
    for (const std::string& str : command)
    {
        ans += (str + skip);
    }
    return ans;
}

// 解析特殊字符串，如环境变量字符串、特殊路径字符串
std::string& parseString(const std::string& str)
{
    // 用于存储解析出来的字符串
    static std::string ans = std::string();
    ans.clear();
    // 判断当前字符串是否为环境变量字符串（在命令中被“${”及“}”括起来）
    bool if_env = false;
    bool if_first_in_env = false;
    for (std::int32_t i = 0; i < str.size(); i++)
    {
        char cur_ch = str[i];
        if (if_env)
        {
            // 辅助解析环境变量的字符串
            static std::string assist = std::string();
            if (cur_ch == '{' || if_first_in_env)
            {
                assist.clear();
                if_first_in_env = false;
                continue;
            }
            else if (cur_ch == '}')
            {
                if_env = false;
                if (!assist.empty())
                {
                    // 获取变量的值
                    char* cur_env_value = getenv(assist.c_str());
                    // 不存在assist环境变量，则尝试assist作为别名(或变量)时的值
                    if (cur_env_value == nullptr)
                    {
                        if (mysh::alias.count(assist))
                        {
                            ans += mysh::alias[assist];
                        }
                        else
                        {
                            ans = str;
                            std::cerr << "\033[31m" << "不存在可替换字符串: " << assist << "\033[0m" << std::endl;
                        }
                    }
                    else
                    {
                        ans += cur_env_value;
                    }
                    assist.clear();
                }
                else
                {
                    ans = str;
                }
                continue;
            }
            else
            {
                assist += cur_ch;
            }
            if (i == str.size() - 1 && !assist.empty())
            {
                assist.clear();
                ans = str;
            }
        }
        else
        {
            // 替换路径
            if (cur_ch == '~')
            {
                ans += mysh::unique_path[cur_ch];
            }
            // 转为解析环境变量字符串
            else if (cur_ch == '$')
            {
                if_env = true;
                if_first_in_env = true;
                continue;
            }
            else
            {
                ans += cur_ch;
            }
        }
    }
    return ans;
}

// 替换字符串，可将传入的字符串替换为别名(或变量)、环境变量、特殊路径的值
void replaceString(std::string& str)
{
    // 若可替换，则先替换为环境变量值或特殊路径
    str = mysh::parseString(str);
    // 再替换为别名(或变量)，别名(或变量)可能存在多次赋值，故用while循环进行多次替换
    while (mysh::alias.count(str))
    {
        str = mysh::alias[str];
    }
}
MY_SHELL_NAMESPACE_END